#include "ethernet.h"
#include "utils.h"
#include "driver.h"
#include "arp.h"
#include "ip.h"
/**
 * @brief 处理一个收到的数据包
 * 
 * @param buf 要处理的数据包
 */
void ethernet_in(buf_t *buf)
{
    // 如果数据包大小小于eth头部长度，直接丢弃
    if(buf->len < sizeof(ether_hdr_t))  return;

    // 获取eth头
    ether_hdr_t *hdr = (ether_hdr_t *)buf->data;

    /*  如果报文的类型/长度字段表示帧长度而不是上层协议类型，
        它会将整个帧交付给网络层或其他更高层次的协议进行处理，
        通常是IP层作为默认情况.

        在这种情况下，网络层可以使用帧长度字段来确定下一步要采取什么操作。
        例如，如果接收者期望接收到UDP数据包，
        它可以检查接收到的以太网帧的长度是否与期望的UDP数据包长度匹配，
        如果两者相等，则该帧可能包含UDP数据包，
        并且可以将该帧传递给UDP层进行进一步处理。

        以太网协议中类型字段取值在0x05DC与0x0600之间的数据帧是保留字段，
        如果出现这样的数据帧是不合法的，应该直接丢弃之
    */
    net_protocol_t proto;   
    if(swap16(hdr->protocol16) <= 0x5dc)        proto = NET_PROTOCOL_IP;
    else if(swap16(hdr->protocol16) > 0x0600)   proto = swap16(hdr->protocol16);
    else                                        return;

    // 去除eth头部
    buf_remove_header(buf, sizeof(ether_hdr_t));

    // 向上层传递数据包
    net_in(buf, proto, hdr->src);
}
/**
 * @brief 处理一个要发送的数据包
 * 
 * @param buf 要处理的数据包
 * @param mac 目标MAC地址
 * @param protocol 上层协议
 */
void ethernet_out(buf_t *buf, const uint8_t *mac, net_protocol_t protocol)
{
    // 首先判断数据长度，如果不足46则显式填充0
    if(buf->len < ETHERNET_MIN_TRANSPORT_UNIT)  buf_add_padding(buf, ETHERNET_MIN_TRANSPORT_UNIT-buf->len);

    // 添加以太网包头
    buf_add_header(buf, sizeof(ether_hdr_t));
    ether_hdr_t *hdr = (ether_hdr_t *)buf->data;

    // 填写目的MAC地址和源MAC地址
    memcpy(hdr->dst, mac, NET_MAC_LEN);
    memcpy(hdr->src, net_if_mac, NET_MAC_LEN);

    // 填写协议类型
    hdr->protocol16 = swap16(protocol);

    // 将添加了以太网包头的数据帧发送到设备驱动层
    driver_send(buf);
}
/**
 * @brief 初始化以太网协议
 * 
 */
void ethernet_init()
{
    buf_init(&rxbuf, ETHERNET_MAX_TRANSPORT_UNIT + sizeof(ether_hdr_t));
}

/**
 * @brief 一次以太网轮询
 * 
 */
void ethernet_poll()
{
    if (driver_recv(&rxbuf) > 0)
        ethernet_in(&rxbuf);
}
